---
title: 内容集合
description: >-
  类型安全的管理你的内容。
i18nReady: true
---
import { FileTree, CardGrid, LinkCard, Steps } from '@astrojs/starlight/components';
import Since from '~/components/Since.astro'
import RecipeLinks from "~/components/RecipeLinks.astro"
import Badge from "~/components/Badge.astro"
import ReadMore from "~/components/ReadMore.astro"

<p><Since v="2.0.0" /></p>

**内容集合（Content collections）** 是在任何 Astro 项目中管理内容集的最佳方式。集合有助于组织和查询文档，为你的编辑器启用智能提示和类型检查，并为所有内容提供自动的 TypeScript 类型安全。

Astro 5.0 引入了内容层（Content Layer）API，用于定义和查询内容集。这个高性能、可扩展的 API 为本地集合提供了内置的内容加载器（content loaders）。对于远程内容，你可以使用第三方和社区构建的加载器，或者创建自己的自定义加载器，从任何来源拉取数据。

:::note
项目可以继续使用 Astro v2.0 中引入的传统内容集合 API。但是，我们鼓励尽可能 [更新任何现有集合](/zh-cn/guides/upgrade-to/v5/#旧版v20-内容集合-api)。
:::

## 什么是内容集合？

你可以从结构相似的数据集中定义一个**集合**。这可以是一个博客文章的目录，一个产品项目的 JSON 文件，或者任何代表相同形状的多个项目的数据。

本地存储在项目中或文件系统上的集合可以包含 Markdown、MDX、Markdoc、YAML、TOML 或 JSON 文件的条目：

<FileTree>
- src/
- **newsletter/** "newsletter" 集合
  - week-1.md 一个集合条目
  - week-2.md 一个集合条目
  - week-3.md 一个集合条目
- **authors/** "author" 集合
  - authors.json 包含所有集合条目的单个文件
</FileTree>

使用对应的集合加载器，你可以从任何外部来源获取远程数据，比如 CMS、数据库或无头支付系统。

## 集合的 TypeScript 配置

内容集合依靠 TypeScript 为你的编辑器提供 Zod 验证、智能提示和类型检查。如果你没有扩展 Astro 的 `strict` 或 `strictest` TypeScript 设置，你需要确保在 `tsconfig.json` 中设置以下 `compilerOptions`：

```json title="tsconfig.json" ins={5} {6}
{
  // 包括在 "astro/tsconfigs/strict" 或 "astro/tsconfigs/strictest" 中
  "extends": "astro/tsconfigs/base",
  "compilerOptions": {
    "strictNullChecks": true, // 使用 `base` 模板需要添加
    "allowJs": true // 必需，包含在所有 Astro 模板中
  }
}
```

## 定义集合

单个集合使用 `defineCollection()` 配置：

- 一个 `loader` 用于数据源（必需）
- 一个 `schema` 用于类型安全（可选，但强烈推荐！）

### 集合配置文件

要定义集合，你必须在项目中创建一个 `src/content.config.ts` 文件（也支持 `.js` 和 `.mjs` 扩展名）。这是一个特殊的文件，Astro 将根据以下结构使用它来配置你的内容集合：

```ts title="src/content.config.ts"
// 1. 从 `astro:content` 导入工具函数
import { defineCollection, z } from 'astro:content';

// 2. 导入加载器
import { glob, file } from 'astro/loaders';

// 3. 定义你的集合
const blog = defineCollection({ /* ... */ });
const dogs = defineCollection({ /* ... */ });

// 4. 导出一个 `collections` 对象来注册你的集合
export const collections = { blog, dogs };
```

### 定义集合 `loader`

内容层 API 允许你从任何地方获取你的内容（无论是本地存储在项目中还是远程存储），并使用 `loader` 属性来检索你的数据。

#### 内置的加载器

Astro 提供了 [两个内置的加载器函数（`glob()` 和 `file()`）](/zh-cn/reference/content-loader-reference/#内置的加载器) 用于获取本地内容，也可以通过 API 来构建自己的加载器并请求远程数据。

[`glob()` 加载器](/zh-cn/reference/content-loader-reference/#glob-加载器) 可以从文件系统的任何地方创建 Markdown、MDX、Markdoc、JSON、YAML 或 TOML 文件的目录条目。它基于 [micromatch](https://github.com/micromatch/micromatch#matching-features) 的 glob 模式支持来接受一个匹配条目文件的 `pattern`，以及你的文件所在的 `base` 文件路径。每个条目的 `id` 将从其文件名自动生成。当每个条目对应一个文件时，请使用此加载器。

[`file()` 加载器](/zh-cn/reference/content-loader-reference/#file-加载器) 从单个本地文件创建多个条目。文件中的每个条目必须有一个唯一的 `id` 键属性。它接受一个 相对你的文件的 `base` 文件路径，以及一个可选的 [`parser` 函数](#parser-函数) 用于它无法自动解析的数据文件。当你的数据文件可以解析为对象数组时，请使用此加载器。

```ts  title="src/content.config.ts" {5,9}
import { defineCollection, z } from 'astro:content';
import { glob, file } from 'astro/loaders'; // 不适用于旧版 API

const blog = defineCollection({
  loader: glob({ pattern: "**/*.md", base: "./src/data/blog" }),
  schema: /* ... */
});
const dogs = defineCollection({
  loader: file("src/data/dogs.json"),
  schema: /* ... */
});

const probes = defineCollection({
  // `loader` 可以接受多个模式的数组以及字符串模式"
  // 加载 space-probes 目录中的所有 Markdown 文件，以 "voyager-" 开头的文件除外
  loader: glob({ pattern: ['*.md', '!voyager-*'], base: 'src/data/space-probes' }),
  schema: z.object({
    name: z.string(),
    type: z.enum(['Space Probe', 'Mars Rover', 'Comet Lander']),
    launch_date: z.date(),
    status: z.enum(['Active', 'Inactive', 'Decommissioned']),
    destination: z.string(),
    operator: z.string(),
    notable_discoveries: z.array(z.string()),
  }),
});

export const collections = { blog, dogs, probes };
```

##### `parser` 函数

`file()` 加载器接受一个第二个参数，定义了一个 `parser` 函数。这允许你指定一个自定义解析器（例如 `csv-parse`）来从文件内容创建一个集合。

`file()` 加载器将自动检测和解析（基于它们的文件扩展名）JSON 和 YAML 文件中的单个对象数组，并将每个顶层表作为 TOML 文件中的独立条目处理。这些文件类型的支持是内置的，除非你有[嵌套的 JSON 文件](#嵌套的-json-文件)，否则不需要 `parser`。要使用其他文件，例如 `.csv`，你需要创建一个解析器函数。

下面的示例展示了如何导入一个 CSV 解析器，然后通过将文件路径和 `parser` 函数都传递给 `file()` 加载器，将名为 `cats` 的集合加载到你的项目中。

```typescript title="src/content.config.ts"
import { defineCollection } from "astro:content";
import { file } from "astro/loaders";
import { parse as parseCsv } from "csv-parse/sync";

const cats = defineCollection({
  loader: file("src/data/cats.csv", { parser: (text) => parseCsv(text, { columns: true, skipEmptyLines: true })})
});
```

###### 嵌套的 `.json` 文件

`parser` 参数还允许你从嵌套的 JSON 文档中加载单个集合。例如，这个 JSON 文件包含了多个集合：

```json title="src/data/pets.json"
{"dogs": [{}], "cats": [{}]}
```

你可以通过为每个集合传递一个自定义的 `parser` 来将这些集合分开：

```typescript title="src/content.config.ts"
const dogs = defineCollection({
  loader: file("src/data/pets.json", { parser: (text) => JSON.parse(text).dogs })
});
const cats = defineCollection({
  loader: file("src/data/pets.json", { parser: (text) => JSON.parse(text).cats })
});
```

#### 构建一个自定义加载器

你可以构建一个自定义加载器来从任何数据源（如 CMS、数据库或 API 端点）获取远程内容。

使用加载器请求数据将自动从远程数据创建一个集合。这为你提供了所有本地集合的好处，例如集合特定的 API 助手，如 `getCollection()` 和 `render()` 来查询和显示你的数据，以及模式验证。

:::tip
在 [Astro 集成目录](https://astro.build/integrations/?search=&categories%5B%5D=loaders) 中查找社区构建和第三方加载器。
:::

##### 内联加载器

你可以在集合内部定义一个内联加载器，作为一个返回条目数组的异步函数。

这对不需要手动控制数据如何加载和存储的加载器非常有用。每当调用加载器时，它都会清除存储并重新加载所有条目。

```ts title="src/content.config.ts"
const countries = defineCollection({
  loader: async () => {
    const response = await fetch("https://restcountries.com/v3.1/all");
    const data = await response.json();
    // 必须返回具有 id 属性的条目数组，或以 ID 作为键、条目作为值的对象
    return data.map((country) => ({
      id: country.cca3,
      ...country,
    }));
  },
  schema: /* ... */
});
```

返回的条目将存储在集合中，并可以使用 `getCollection()` 和 `getEntry()` 函数进行查询。

##### 加载器对象

为了更好的控制加载过程，你可以使用内容加载器 API 来创建一个加载器对象。例如，通过直接访问 `load` 方法，你可以创建一个加载器，允许条目逐步更新或仅在必要时清除存储。

与创建 Astro 集成或 Vite 插件类似，你可以将你的加载器 [作为 NPM 包分发](/zh-cn/reference/publish-to-npm/)，供他人在他们的项目中使用。

<ReadMore>更多关于构建你自己的加载器的例子，请参考完整的 [内容加载器 API](/zh-cn/reference/content-loader-reference/)。</ReadMore>

### 定义集合模式（Schema）

模式通过 Zod 验证强制执行集合中的一致的 frontmatter 或条目数据。模式 **保证** 了当你需要引用或查询数据时，这些数据以可预测的形式存在。如果任何文件违反了其集合模式，Astro 将提供一个有用的错误提示。

模式还为 Astro 的内容自动 TypeScript 类型提供了支持。当你为集合定义模式时，Astro 将自动生成并应用一个 TypeScript 接口。结果是当你查询集合时，完整的 TypeScript 支持，包括属性自动补全和类型检查。

:::tip
为了让 Astro 识别新的或更新后的模式，你可能需要重启开发服务器，或者 [同步内容层](/zh-cn/reference/cli-reference/#astro-dev) (<code>s + enter</code>) 来定义 `astro:content` 模块。
:::

集合条目的每个 frontmatter 或数据属性都必须使用 Zod 数据类型定义：

```ts title="src/content.config.ts" {6-11,15-19}
import { defineCollection, z } from 'astro:content';
import { glob, file } from 'astro/loaders'; // 不适用于旧版 API

const blog = defineCollection({
  loader: glob({ pattern: "**/*.md", base: "./src/data/blog" }),
  schema: z.object({
    title: z.string(),
    description: z.string(),
    pubDate: z.coerce.date(),
    updatedDate: z.coerce.date().optional(),
  })
});
const dogs = defineCollection({
  loader: file("src/data/dogs.json"),
  schema: z.object({
    id: z.string(),
    breed: z.string(),
    temperament: z.array(z.string()),
  }),
});

export const collections = { blog, dogs };
```

#### 使用 Zod 定义数据类型

Astro 使用 [Zod](https://github.com/colinhacks/zod) 来支持其内容模式。使用 Zod，Astro 能够验证集合中每个文件的数据，并在你查询内容时提供自动的 TypeScript 类型。

要在 Astro 中使用 Zod，请从 `"astro:content"` 导入 `z` 工具函数。这是 Zod 库的重新导出，支持 Zod 的所有功能。

```ts
// 示例：许多常见 Zod 数据类型的备忘单
import { z, defineCollection } from 'astro:content';

defineCollection({
  schema: z.object({
    isDraft: z.boolean(),
    title: z.string(),
    sortOrder: z.number(),
    image: z.object({
      src: z.string(),
      alt: z.string(),
    }),
    author: z.string().default('Anonymous'),
    language: z.enum(['en', 'es']),
    tags: z.array(z.string()),
    footnote: z.string().optional(),

    // 在 YAML 中，不带引号的日期被解释为 Date 对象
    publishDate: z.date(), // 例如 2024-09-17

    // 将日期字符串（例如 “2022-07-08”）转换为 Date 对象
    updatedDate: z.string().transform((str) => new Date(str)),

    authorContact: z.string().email(),
    canonicalURL: z.string().url(),
  })
})
```

<ReadMore>有关 Zod 如何工作及其可用功能的完整文档，请参阅 [Zod 的 README](https://github.com/colinhacks/zod)。</ReadMore>

##### Zod 模式方法

所有 [Zod 模式方法](https://zod.dev/?id=schema-methods)（例如 `.parse()`、`.transform()`）都可用，但有一些限制。特别是，使用 `image().refine()` 对图像执行自定义验证检查是不支持的。

#### 定义集合引用

集合条目还可以“引用”其他相关条目。这对于在集合模式中定义属性引用其他集合的条目非常有用。

使用集合 API 中的 [`reference()` 函数](/zh-cn/reference/modules/astro-content/#reference)，你可以将集合模式中的属性定义为来自另一个集合的条目。例如，你可以要求每个 `space-shuttle` 条目都包含一个 `pilot` 属性，该属性使用 `pilot` 集合自己的模式进行类型检查、自动补全和验证。

一个常见的例子是一个引用存储为 JSON 的可重用作者配置文件的博客文章，或者存储在同一集合中的相关文章 URL：

```ts title="src/content.config.ts"
import { defineCollection, reference, z } from 'astro:content';
import { glob } from 'astro/loaders';

const blog = defineCollection({
  loader: glob({ pattern: '**/[^_]*.md', base: "./src/data/blog" }),
  schema: z.object({
    title: z.string(),
    // 通过 `id` 从 `authors` 集合引用单个作者
    author: reference('authors'),
    // 通过 `slug` 从 `blog` 集合引用相关文章数组
    relatedPosts: z.array(reference('blog')),
  })
});

const authors = defineCollection({
  loader: glob({ pattern: '**/[^_]*.json', base: "./src/data/authors" }),
  schema: z.object({
    name: z.string(),
    portfolio: z.string().url(),
  })
});

export const collections = { blog, authors };
```

这个博客文章示例指定了相关文章的 `id` 和作者的 `id`：

```yaml title="src/data/blog/welcome.md"
---
title: "Welcome to my blog"
author: ben-holmes # references `src/data/authors/ben-holmes.json` 
relatedPosts:
- about-me # references `src/data/blog/about-me.md`
- my-year-in-review # references `src/data/blog/my-year-in-review.md`
---
```

这些引用将被转换为包含 `collection` 键和 `id` 键的对象，使你可以轻松地[在模板中查询它们](/zh-cn/guides/content-collections/#访问引用数据)。

### 定义自定义 ID

当将 Markdown、MDX、Markdoc 或 JSON 文件与 `glob()` 加载器一起使用时，每个内容条目的 [`id`](/zh-cn/reference/modules/astro-content/#id) 将根据内容文件名自动生成一个 URL 友好的格式。`id` 用于直接从你的集合中查询条目。它还在从内容创建新页面和 URL 时非常有用。

你可以通过在文件的 frontmatter 或 JSON 文件的数据对象中添加自己的 `slug` 属性来覆盖条目生成的 `id`。这类似于其他 Web 框架的 “permalink” 功能。

```md title="src/blog/1.md" {3}
---
title: My Blog Post
slug: my-custom-id/supports/slashes
---
Your blog post content here.
```

```json title="src/categories/1.json" {3}
{
  "title": "My Category",
  "slug": "my-custom-id/supports/slashes",
  "description": "Your category description here."
}
```

## 查询集合

Astro 提供辅助函数来查询集合并返回一个（或多个）内容条目。

- [`getCollection()`](/zh-cn/reference/modules/astro-content/#getcollection) 获取整个集合并返回一个条目数组。
- [`getEntry()`](/zh-cn/reference/modules/astro-content/#getentry) 从集合中获取单个条目。

它们返回具有唯一 `id`、包含所有定义属性的 `data` 对象，并且还将返回一个 `body`，其中包含 Markdown、MDX 或 Markdoc 文档的原始、未编译的正文。

```js
import { getCollection, getEntry } from 'astro:content';

// 获取集合中的所有条目。
// 需要集合的名称作为参数。
const allBlogPosts = await getCollection('blog');

// 从集合中获取单个条目。
// 需要集合的名称和 `id`
const poodleData = await getEntry('dogs', 'poodle');
```

生成后的集合条目顺序是非确定性的且依赖于运行平台。这意味着如果你在调用 `getCollection()` 时，若需以特定顺序返回条目（例如，按日期顺序来排序博客文章），必须自行对集合条目进行排序：

```astro title="src/pages/blog.astro"
---
import { getCollection } from 'astro:content';

const posts = (await getCollection('blog')).sort(
  (a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf(),
);
---
```

<ReadMore>请参阅 [`CollectionEntry` 类型](/zh-cn/reference/modules/astro-content/#collectionentry) 返回的完整属性列表。</ReadMore>

### 在 Astro 模板中使用内容

查询集合后，你可以直接在 Astro 组件模板中访问每个条目的内容。例如，你可以创建一个链接列表，显示条目的 frontmatter 信息，使用 `data` 属性。

```astro title="src/pages/index.astro"
---
import { getCollection } from 'astro:content';
const posts = await getCollection('blog');
---
<h1>My posts</h1>
<ul>
  {posts.map(post => (
    <li><a href={`/blog/${post.id}`}>{post.data.title}</a></li>
  ))}
</ul>
```
#### 渲染正文内容

查询后，你可以使用 [`render()`](/zh-cn/reference/modules/astro-content/#render) 函数属性将 Markdown 和 MDX 条目渲染为 HTML。调用此函数将使你可以访问渲染的 HTML 内容，包括 `<Content />` 组件和所有已渲染标题的列表。

```astro title="src/pages/blog/post-1.astro" {5,8}
---
import { getEntry, render } from 'astro:content';

const entry = await getEntry('blog', 'post-1');
if (!entry) {
  // 处理错误，例如：
  throw new Error('Could not find blog post 1');
}
const { Content, headings } = await render(entry);
---
<p>Published on: {entry.data.published.toDateString()}</p>
<Content />
```

<ReadMore>在处理 MDX 条目时，你也可以 [将自定义组件传递给 `<Content />`](/zh-cn/guides/integrations-guide/mdx/#将-components-传给-mdx-内容)，使用自定义组件替换 HTML 元素。</ReadMore>

#### 将内容作为 props 传递

组件还可以将整个集合条目作为 prop 传递。

你可以使用 [`CollectionEntry`](/zh-cn/reference/modules/astro-content/#collectionentry) 工具函数来使用 TypeScript 正确地为组件的 props 添加类型。此函数接受一个字符串参数，该参数与你的集合模式的名称匹配，并将继承该集合模式的所有属性。

```astro title="src/components/BlogCard.astro" /CollectionEntry(?:<.+>)?/
---
import type { CollectionEntry } from 'astro:content';
interface Props {
  post: CollectionEntry<'blog'>;
}

// `post` 将匹配你的 'blog' 集合模式类型
const { post } = Astro.props;
---
```

### 过滤集合查询

`getCollection()` 采用一个可选的 "filter" 回调，允许你根据条目的 `id` 或 `data` 属性过滤查询。

你可以使用它来根据你喜欢的任何内容标准进行过滤。例如，你可以通过 `draft` 属性来过滤，以防止任何草稿博客文章发布到你的博客：

```js
// 示例：使用 `draft: true` 过滤掉内容条目
import { getCollection } from 'astro:content';
const publishedBlogEntries = await getCollection('blog', ({ data }) => {
  return data.draft !== true;
});
```

你也可以创建在运行开发服务器时可用但不在生产中构建的草稿页面。

```js
// 示例：仅在为生产构建时过滤掉带有 `draft: true` 的内容条目
import { getCollection } from 'astro:content';
const blogEntries = await getCollection('blog', ({ data }) => {
  return import.meta.env.PROD ? data.draft !== true : true;
});
```

过滤参数还支持按照集合中的嵌套目录进行过滤。由于 `id` 包含完整的嵌套路径，你可以通过每个 `id` 的开始来过滤，以仅返回特定嵌套目录中的条目：

```js
// 示例：按集合中的子目录过滤条目
import { getCollection } from 'astro:content';
const englishDocsEntries = await getCollection('docs', ({ id }) => {
  return id.startsWith('en/');
});
```

### 访问引用数据

首次查询集合条目后，必须单独查询 [模式中定义的任何引用](/zh-cn/guides/content-collections/#定义集合引用)。由于 [`reference()` 函数](/zh-cn/reference/modules/astro-content/#reference) 将一个引用转换为包含 `collection` 和 `id` 作为键的对象，你可以使用 `getEntry()` 函数返回单个引用项，或者使用 `getEntries()` 从返回的 `data` 对象中检索多个引用条目。

```astro title="src/pages/blog/welcome.astro"
---
import { getEntry, getEntries } from 'astro:content';

const blogPost = await getEntry('blog', 'welcome');

// 解析单一引用(例如：`{collection: "authors", id: "ben-holmes"}`)
const author = await getEntry(blogPost.data.author);
// 解析引用数组
// (例如：`[{collection: "blog", id: "about-me"}, {collection: "blog", id: "my-year-in-review"}]`)
const relatedPosts = await getEntries(blogPost.data.relatedPosts);
---

<h1>{blogPost.data.title}</h1>
<p>Author: {author.data.name}</p>

<!-- ... -->

<h2>You might also like:</h2>
{relatedPosts.map(post => (
  <a href={post.id}>{post.data.title}</a>
))}
```

## 从内容生成路由

内容集合存储在 `src/pages/` 目录之外。这意味着默认情况下不会为集合条目生成页面或路由。

如果你想要为每个集合条目生成 HTML 页面或路由，例如单独的博客文章，你需要手动创建一个新的 [动态路由](/zh-cn/guides/routing/#动态路由)。你的动态路由将把传入的请求参数（例如 `Astro.params.slug` 在 `src/pages/blog/[...slug].astro` 中）映射到每个页面的正确条目。

生成路由的确切方法取决于你的页面是预渲染的（默认）还是由服务器按需渲染的。

### 构建静态输出（默认）

如果你正在构建一个静态网站（Astro 的默认行为），请使用 [`getStaticPaths()`](/zh-cn/reference/routing-reference/#getstaticpaths) 函数在构建期间从单个页面组件（例如 `src/pages/[slug]`）创建多个页面。

在 `getStaticPaths()` 中调用 `getCollection()`，以便在构建静态路由时为你的集合数据提供支持。然后，使用每个内容条目的 `id` 属性创建单独的 URL 路径。每个页面都将整个集合条目作为 prop 传递，以便在 [页面模板中使用](#在-astro-模板中使用内容)。

```astro title="src/pages/posts/[id].astro" "{ id: post.id }" "{ post }"
---
import { getCollection, render } from 'astro:content';
// 1. 为每个集合条目生成一个新路径
export async function getStaticPaths() {
  const posts = await getCollection('blog');
  return posts.map(post => ({
    params: { id: post.id },
    props: { post },
  }));
}
// 2. 对于你的模板，你可以直接从 prop 获取条目
const { post } = Astro.props;
const { Content } = await render(post);
---
<h1>{post.data.title}</h1>
<Content />
```

这将为 `blog` 集合中的每个条目生成一个页面路由。例如，`src/blog/hello-world.md` 中的条目将具有 `hello-world` 的 `id`，因此其最终 URL 将是 `/posts/hello-world/`。

:::note
如果你的自定义 slug 包含 `/` 字符以生成具有多个路径段的 URL，你必须在 `.astro` 文件名中使用 [rest 参数（例如 `[...slug]`）](/zh-cn/guides/routing/#剩余参数) 用于这个动态路由页面。
:::

### 构建服务器输出 (SSR)

如果你正在构建动态网站（使用 Astro 的 SSR 支持），你不需要在构建期间提前生成任何路径。相反，你的页面应该在请求时检查请求（使用 `Astro.request` 或 `Astro.params`）以找到 `slug`，然后使用 [`getEntry()`](/zh-cn/reference/modules/astro-content/#getentry) 来按需请求它。

```astro title="src/pages/posts/[id].astro"
---
import { getEntry, render } from "astro:content";
// 1. 从传入的服务器请求中获取 slug
const { id } = Astro.params;
if (id === undefined) {
	return Astro.redirect("/404");
}
// 2. 直接使用请求 slug 查询条目
const post = await getEntry("blog", id);
// 3. 如果条目不存在则重定向
if (post === undefined) {
	return Astro.redirect("/404");
}
// 4. 将条​​目渲染为模板中的 HTML
const { Content } = await render(post);
---
<h1>{post.data.title}</h1>
<Content />
```

:::tip
探索 [GitHub 上博客教程演示代码的 `src/pages/` 文件夹](https://github.com/withastro/blog-tutorial-demo/tree/content-collections/src/pages) ，查看如何从你的集合创建页面，例如博客文章列表、标签页面等的完整示例！
:::

## 内容集合的 JSON 模式（Schema）

<p><Since v="4.13.0" /></p>

Astro 会为内容集合自动生成 [JSON 模式](https://json-schema.org/) 文件，你可以在编辑器中利用这些文件获取数据文件的智能提示和类型检查功能。

项目中每个内容集合都会生成对应的 JSON 模式文件，并输出至 `.astro/collections/` 目录。例如，若你拥有两个分别名为 `authors` 和 `posts` 的集合，那么 Astro 将会生成：`.astro/collections/authors.schema.json` 和 `.astro/collections/posts.schema.json`。

### 在 JSON 文件中使用 JSON 模式

你可以通过在 JSON 文件中设置 `$schema` 字段来手动指向 Astro 生成的模式文件。该值应为从数据文件到模式文件的相对路径。在以下示例中，位于 `src/data/authors/` 目录下的数据文件使用了为 `authors` 集合生成的模式：

```json title="src/data/authors/armand.json" ins={2}
{
  "$schema": "../../../.astro/collections/authors.schema.json",
  "name": "Armand",
  "skills": ["Astro", "Starlight"]
}
```

#### 在 VS Code 中为一组 JSON 文件配置统一模式 

在 VS Code 中，你可以通过 [`json.schemas` 设置](https://code.visualstudio.com/docs/languages/json#_json-schemas-and-settings) 配置模式文件，使其自动应用于集合中的所有文件。在以下示例中，`src/data/authors/` 目录下的所有文件都将使用为 `authors` 集合生成的模式文件：

```json
{
  "json.schemas": [
    {
      "fileMatch": ["/src/data/authors/**"],
      "url": "./.astro/collections/authors.schema.json"
    }
  ]
}
```

### 在 VS Code 中为 YAML 文件配置模式

在 VS Code 中，你可以通过安装 [Red Hat YAML](https://marketplace.visualstudio.com/items?itemName=redhat.vscode-yaml) 扩展来实现在 YAML 文件中使用 JSON 模式。安装此扩展后，你可以使用特殊的注释语法在 YAML 文件中引用模式：

```yaml title="src/data/authors/armand.yml" ins={1}
# yaml-language-server: $schema=../../../.astro/collections/authors.schema.json
name: Armand
skills:
  - Astro
  - Starlight
```

#### 在 VS Code 中为一组 YAML 文件配置统一模式

通过 Red Hat YAML 扩展，你可以使用 `yaml.schemas` 设置配置模式文件，使其自动应用于集合中的所有 YAML 文件。在以下示例中，`src/data/authors/` 目录下的所有 YAML 文件都将使用为 `authors` 集合生成的模式文件：

```json
{
  "yaml.schemas": {
    "./.astro/collections/authors.schema.json": ["/src/content/authors/*.yml"]
  }
}
```

更多细节可参阅 Red Hat YAML 扩展文档的 ["Associating schemas"](https://marketplace.visualstudio.com/items?itemName=redhat.vscode-yaml#associating-schemas) 部分。

## 何时创建集合

只要你拥有一组共享相同结构的相关数据或内容，你就可以[创建一个集合](#定义集合)。

使用集合的大部分好处来自于：

- 定义通用数据形态，以验证单个条目是否 “正确” 或 “完整”，避免在生产中出现错误。
- 以内容为中心的 API 旨在当在页面上导入和渲染内容时，使查询变得直观（例如 `getCollection()` 而不是 `import.meta.glob()`）。
- 用于检索内容的 [内容加载器 API](/zh-cn/reference/content-loader-reference/)，提供了内置加载器和可用的底层 API。这里有一些第三方和社区构建的加载器可用，你可以构建自己的自定义加载器，从任何地方获取数据。
- 性能和可扩展性。内容层 API 允许数据在构建之间缓存，并适用于数万条内容条目。

在以下情况下，将 [数据定义为集合](#定义集合)：

- 你有多个文件或数据需要组织，共享相同的整体结构（例如，使用 Markdown 编写的博客文章，这些文章都具有相同的 frontmatter 属性）。
- 你有远程存储的现有内容，例如在 CMS 中，想要利用集合辅助函数和内容层 API，而不是使用 `fetch()` 或 SDK。
- 你需要请求（数万）个相关数据，并且需要一个能够处理大规模查询和缓存的方法。

### 什么时候不要使用集合

当你有 **多个内容需要共享相同的属性** 时，集合提供了出色的结构、安全性和组织。

以下情况，集合 **可能不是你的解决方案**：

- 你只有一个或少量不同的页面。考虑直接使用你的内容 [创建单独的页面组件](/zh-cn/basics/astro-pages/)，例如 `src/pages/about.astro`。
- 你正在显示不由 Astro 处理的文件，例如 PDF。将这些静态资产放在项目的 [`public/` 目录](/zh-cn/basics/project-structure/#public)中。
- 你的数据源有自己的 SDK/客户端库用于导入，与内容加载器不兼容或不提供内容加载器，并且你更喜欢直接使用它。
- 你正在使用需要实时更新的 API。内容集合仅在构建时更新，因此如果你需要实时数据，请使用其他方法 [导入文件](/zh-cn/guides/imports/#import-语句) 或 使用 [按需渲染](/zh-cn/guides/on-demand-rendering/) 来 [请求数据](/zh-cn/guides/data-fetching/)。
